package sys

import (
	"context"
	"errors"
	"fmt"
	"github.com/google/wire"
	"go.manyji.com/lib/log"
	"go.manyji.com/lib/mysql"
	"sieve_admin_server/app/dao"
	"sieve_admin_server/app/dao/menu"
	"sieve_admin_server/app/dao/role"
	"sieve_admin_server/app/schema"
	"strconv"
	"strings"
	"time"
)

var (
	RoleSet = wire.NewSet(wire.Struct(new(RoleSrv), "*"))
)

type RoleSrv struct {
	RoleRepo     *dao.RoleRepo
	Trans        *dao.Trans
	RoleMenuRepo *dao.RoleMenuRepo
	MenuSrv      *MenuSrv
}

var (
	adminRole = &role.Role{
		Name:  "管理员",
		Alias: "admin",
	}

	salespersonRole = &role.Role{
		Name:  "业务员",
		Alias: "salesperson",
	}

	agentRole = &role.Role{
		Name:  "代理商",
		Alias: "agent",
	}

	channelAgentRole = &role.Role{
		Name:  "渠道主",
		Alias: "channelAgent",
	}
)

func (a *RoleSrv) IsAdminRoleId(rId uint64) bool {

	return adminRole.ID == int64(rId)
}

func (a *RoleSrv) GetAdminRole() *role.Role {

	return adminRole
}

func (a *RoleSrv) GetAgentRole() *role.Role {
	return agentRole
}

func (a *RoleSrv) GetSalespersonRole() *role.Role {

	return salespersonRole
}

func (a *RoleSrv) IsSalespersonRole(rId uint64) bool {

	return salespersonRole.ID == int64(rId)
}

func (a *RoleSrv) IsAgentRole(rId uint64) bool {

	return agentRole.ID == int64(rId)
}

func (a *RoleSrv) IsChannelAgentRole(rId uint64) bool {

	return channelAgentRole.ID == int64(rId)
}

func (a *RoleSrv) GetChannelAgentRole() *role.Role {

	return channelAgentRole
}

func (a *RoleSrv) UpdateAdminRoleId(rId uint64) {

	adminRole.ID = int64(rId)
}

func (a *RoleSrv) GetRoleList(ctx context.Context, rId uint64, ignoreSelfRole bool) []*role.Role {

	if rId == uint64(adminRole.ID) {
		if ignoreSelfRole {
			return []*role.Role{
				//adminRole,
				salespersonRole,
				agentRole,
				channelAgentRole,
			}
		} else {
			return []*role.Role{
				adminRole,
				salespersonRole,
				agentRole,
				channelAgentRole,
			}
		}
	}

	if rId == uint64(salespersonRole.ID) {
		if ignoreSelfRole {
			return []*role.Role{
				salespersonRole,
				agentRole,
				channelAgentRole,
			}
		} else {
			return []*role.Role{
				agentRole,
				channelAgentRole,
			}
		}
	}

	if rId == uint64(agentRole.ID) {
		if ignoreSelfRole {
			return []*role.Role{
				channelAgentRole,
			}
		} else {
			return []*role.Role{
				agentRole,
				channelAgentRole,
			}
		}
	}

	if rId == uint64(channelAgentRole.ID) {
		if ignoreSelfRole {
			return []*role.Role{}
		} else {
			return []*role.Role{
				channelAgentRole,
			}
		}

	}

	return nil
}

func (a *RoleSrv) createRoleMenu(ctx context.Context, r *role.Role, menuIDs []string) (int64, error) {

	err := a.Trans.Exec(ctx, func(ctx context.Context, t *mysql.Tx) error {
		resp := a.Create(ctx, map[string]interface{}{
			"name":  r.Name,
			"alias": r.Alias,
		}, &r.ID)
		if err, ok := resp.(error); ok && err != nil && r.ID == 0 {
			r.ID = 0
			return err
		}

		if err := a.Update(ctx, map[string]interface{}{
			"id":       uint64(r.ID),
			"menu_ids": strings.Join(menuIDs, "|"),
		}); err != nil {
			r.ID = 0
			return err
		}

		return nil
	})
	if err != nil {
		return 0, err
	}

	return r.ID, nil
}

// 创建角色，绑定菜单权限
func (a *RoleSrv) CreateRole(ctx context.Context, args ...interface{}) (int64, error) {

	respAllMenus, err := a.MenuSrv.GetAllMenus(ctx)
	if err != nil {
		return 0, err
	}

	var (
		// 管理员角色
		adminRoleExcludeMenu = map[string]struct{}{
			"number/*":        {},
			"wsSieve/*":       {},
			"fbSieve/*":       {},
			"insSieve/*":      {},
			"linkedinSieve/*": {},
			"amazonSieve/*":   {},
			"viberSieve/*":    {},
		}

		// 代理商角色
		agentRoleExcludeMenu = map[string]struct{}{
			"customer/businessType": {},
			"customer/announcement": {},
			"sys/country":           {},
			"sys/role":              {},
			"sys/notice":            {},
			"log/login":             {},
			"log/business":          {},
		}

		// 渠道主角色
		channelAgentRoleExcludeMenu = map[string]struct{}{
			"customer/user":          {},
			"customer/userRecords":   {},
			"customer/recharge":      {},
			"customer/income":        {},
			"customer/dailyRecharge": {},
			"customer/businessType":  {},
			"customer/announcement":  {},
			"sys/country":            {},
			"sys/role":               {},
			"sys/notice":             {},
			"log/login":              {},
			"log/business":           {},
		}

		// 业务员角色
		salespersonRoleExcludeMenu = map[string]struct{}{
			//"sys/country": {},
			//"sys/role":    {},
			//"sys/notice":  {},
			"sys/*":           {},
			"number/*":        {},
			"wsSieve/*":       {},
			"fbSieve/*":       {},
			"insSieve/*":      {},
			"linkedinSieve/*": {},
			"amazonSieve/*":   {},
			"viberSieve/*":    {},
		}

		excludeMenus = make(map[string]struct{})
		menuIDs      []string
		r            = adminRole
	)

	if len(args) == 1 {

		if args[0].(int) == 1 {
			r = adminRole
			excludeMenus = adminRoleExcludeMenu
		}

		if args[0].(int) == 2 {
			r = salespersonRole
			excludeMenus = salespersonRoleExcludeMenu
		}

		if args[0].(int) == 3 {
			r = agentRole
			excludeMenus = agentRoleExcludeMenu
		}

		if args[0].(int) == 4 {
			r = channelAgentRole
			excludeMenus = channelAgentRoleExcludeMenu
		}
	}

	for _, v := range respAllMenus {
		m := v.(*menu.Menu)
		if _, ok := excludeMenus[fmt.Sprintf("%v/%v", m.Module, m.Item)]; ok {
			continue
		}

		// 模式匹配
		if _, ok := excludeMenus[fmt.Sprintf("%v/%v", m.Module, "*")]; ok {
			continue
		}

		menuIDs = append(menuIDs, fmt.Sprintf("%v", m.ID))
	}

	return a.createRoleMenu(ctx, r, menuIDs)
}

func (a *RoleSrv) Get(ctx context.Context, item map[string]interface{}) ([]interface{}, error) {

	var (
		querySQL = fmt.Sprintf(`
			SELECT
				*
			FROM
			    role
			WHERE 
			    %v = '%v' AND %v = '%v'
`, "`name`", item["name"], "`alias`", item["alias"])
	)

	return a.RoleRepo.QueryRaw(ctx, querySQL, role.Role{})
}

func (a *RoleSrv) Create(ctx context.Context, item map[string]interface{}, args ...interface{}) interface{} {

	respRole, err := a.Get(ctx, item)
	if err != nil {
		return err
	}

	for _, v := range respRole {
		vRole := v.(*role.Role)
		if vRole.Name == item["name"] {
			if len(args) == 1 {
				*args[0].(*int64) = vRole.ID
			}
			return errors.New(fmt.Sprintf("已经存在相同的角色%v", item["name"]))
		}
	}

	var (
		now = time.Now().Unix()
		def = map[string]interface{}{
			"create_at":  now,
			"updated_at": now,
		}
	)

	for k, v := range def {
		item[k] = v
	}

	rowsAffected, err := a.RoleRepo.Create(ctx, item)
	if err != nil {
		return err
	}
	id, _ := rowsAffected.LastInsertId()
	if len(args) == 1 {
		*args[0].(*int64) = id
	}
	return err
}

func (a *RoleSrv) QueryRoleMenu(ctx context.Context, roleId uint64) interface{} {

	return a.MenuSrv.GetMenus(ctx, roleId, true)
}

func (a *RoleSrv) Query(ctx context.Context, reqArgs map[string]interface{}, p schema.PaginationParam, repKeys []string) interface{} {

	if _, ok := reqArgs["name"]; ok {
		ctx = context.WithValue(ctx,
			"condition", map[string]string{"name": "LIKE"},
		)

		reqArgs["name"] = "'%" + reqArgs["name"].(string) + "%'"
	}

	req, err := a.RoleRepo.Query(ctx, reqArgs, p, repKeys)
	if err != nil {
		return err
	}
	return req
}

func (a *RoleSrv) Update(ctx context.Context, reqArgs map[string]interface{}) error {

	if _, ok := reqArgs["menu_ids"]; !ok {
		return a.updateRole(ctx, reqArgs)
	}

	return a.updateRoleMenus(ctx, reqArgs)
}

func (a *RoleSrv) updateRoleMenus(ctx context.Context, reqArgs map[string]interface{}) error {

	var (
		entity      = role.RoleMenu{}
		existsIds   []string
		createItems []map[string]interface{}
		menuIds     = strings.Split(reqArgs["menu_ids"].(string), "|")
	)

	// 传 empty 表示充值角色的菜单权限
	if len(menuIds) == 1 && menuIds[0] == "empty" {
		var (
			deleteAllSQL = fmt.Sprintf(`
	DELETE FROM role_menu WHERE role_id = %v
`, reqArgs["id"])
		)

		if _, err := a.RoleMenuRepo.ExecRaw(context.Background(), deleteAllSQL); err != nil {
			log.Err("delete menus err:%+v", err)
			return err
		}
		return nil
	}

	for _, menuId := range menuIds {
		var (
			roleId = reqArgs["id"].(uint64)
			now    = time.Now()
			item   = map[string]interface{}{
				"role_id":    roleId,
				"menu_id":    menuId,
				"create_at":  now.Unix(),
				"updated_at": now.Unix(),
			}
			checkDupSQL = fmt.Sprintf(`
				SELECT
				 * 
				FROM
				    role_menu
				WHERE
				    role_id = %v AND menu_id = %v AND status = 1
`, roleId, menuId)
		)

		log.Info(fmt.Sprintf("query user menu:%+v", checkDupSQL))

		req, err := a.RoleMenuRepo.QueryRaw(ctx, checkDupSQL, entity)
		if err != nil {
			log.Err("get menus err:%+v", err)
			continue
		}

		if req == nil {
			//if err := a.RoleMenuRepo.Create(context.Background(), item); err != nil {
			//	log.Err("create role menu record err:%+v", err)
			//}

			createItems = append(createItems, item)
			continue
		}

		roleMenu := req[0].(*role.RoleMenu)
		existsIds = append(existsIds, fmt.Sprintf("%v", roleMenu.ID))
		if roleMenu.RoleId == roleId && fmt.Sprintf("%v", roleMenu.MenuId) == menuId {
			continue
		}

		var (
			queries = map[string]interface{}{
				"id": roleMenu.ID,
			}
			updateKeys = map[string]interface{}{
				"role_id":    roleId,
				"menu_id":    menuId,
				"updated_at": now.Unix(),
			}
		)

		if err := a.RoleMenuRepo.Update(context.Background(), updateKeys, queries); err != nil {
			log.Err("update role menu record err:%+v", err)
		}
	}

	if len(existsIds) > 0 {
		var (
			deleteSQL = fmt.Sprintf(`
	DELETE FROM role_menu WHERE role_id = %v And id NOT IN (%v)
`, reqArgs["id"], strings.Join(existsIds, ","))
		)

		if _, err := a.RoleMenuRepo.ExecRaw(context.Background(), deleteSQL); err != nil {
			log.Err("sql:%v,delete role menus err:%+v", deleteSQL, err)
			return err
		}
	}

	for _, item := range createItems {
		if err := a.RoleMenuRepo.Create(context.Background(), item); err != nil {
			log.Err("create role menu err:%+v", err)
		}
	}

	return nil
}

func (a *RoleSrv) updateRole(ctx context.Context, reqArgs map[string]interface{}) error {

	if reqArgs["id"] == adminRole.ID {
		return errors.New(fmt.Sprintf("管理员权限不可以被修改"))
	}

	var (
		now        = time.Now().Unix()
		updateKeys = map[string]interface{}{
			"updated_at": now,
			"alias":      reqArgs["alias"],
			"name":       reqArgs["name"],
		}
		queryKeys = map[string]interface{}{
			"id": reqArgs["id"],
		}
	)

	return a.RoleRepo.Update(ctx, updateKeys, queryKeys)
}

func (a *RoleSrv) Delete(ctx context.Context, reqArgs map[string]interface{}) interface{} {

	var (
		id, _ = strconv.ParseUint(fmt.Sprintf("%v", reqArgs["id"]), 10, 64)
	)

	return a.RoleRepo.SoftDelete(ctx, id)
}
